package net.danielseidl.ludumdare.ld48;

import net.danielseidl.ludumdare.ld48.character.Monster;
import net.danielseidl.ludumdare.ld48.character.Player;
import net.danielseidl.ludumdare.ld48.level.Level;
import net.danielseidl.ludumdare.ld48.tile.TileLoader;
import net.danielseidl.ludumdare.ld48.view.GameView;

/**
 * Main game controller.
 *
 * @author    Daniel Seidl - info@daniel-seidl.net
 */
public class Game extends Thread
	{
	/**
	 * Player instance.
	 */
	private Player player;
	
	/**
	 * Level instance.
	 */
	private Level level;	
	
	/**
	 * Game view instance.
	 */
	private GameView view;
	
	/**
	 * Tile loader instance.
	 */
	private TileLoader loader;
	
	/**
	 * Player escape status.
	 */
	private boolean hasEscaped = false;
	
	/**
	 * Game stop status.
	 */
	private boolean gameStopped = false;
	
	/**
	 * Next map loading status.
	 */
	private boolean nextMapLoading = false;	
	
	/**
	 * Creates a new game instance.
	 * @param player The player instance. 
	 * @param level The level instance.
	 * @param loader The tile loader instance.
	 */
	public Game(Player player, Level level, TileLoader loader)
		{
		this.player = player;
		this.level = level;
		this.loader = loader;
		
		// TODO -> View size should be based on user preferences!
		this.view = new GameView(1024, 768, this);
		
		// Set the player to the level start location.
		this.player.setLocation(this.level.getLevelStartLocation());
		this.view.forceLocationUpdate();
		this.view.forceTileMapUpdate();
		}
	
	/**
	 * Returns the player instance.
	 * @return The player instance.
	 */
	public Player getPlayer()
		{
		return this.player;
		}	
	
	/**
	 * Returns the level instance.
	 * @return The level instance.
	 */
	public Level getLevel()
		{
		return this.level;
		}	
	
	/**
	 * Returns the game view instance.
	 * @return The game view instance.
	 */
	public GameView getGameView()
		{
		return this.view;
		}		
	
	/**
	 * Returns the tile loader instance.
	 * @return The tile loader instance.
	 */
	public TileLoader getTileLoader()
		{
		return this.loader;
		}	
	
	/**
	 * Main game loop.
	 */
	public void run()
		{
		// Force a constant frame rate of 30 frames per second.
		long frame_period = 1000000000L / 30;
		long nextFrameStart = System.nanoTime();
		while (true)
			{
			do
				{
				nextFrameStart += frame_period;
				}
			while(nextFrameStart < System.nanoTime());
					    	
			// Just update the game view if it's time!
			this.view.repaintView();
					    	
			// Let the thread sleep until we need it again...
			long remaining = nextFrameStart - System.nanoTime();
			if (remaining > 0)
				{
				try
					{
					Thread.sleep(remaining / 1000000);
					}
				catch(Throwable t)
					{
					}
				}
			}
		}
	
	/**
	 * Should be called if and only if the player escaped a level.
	 */
	public void playerHasEscaped()
		{
		if (!hasEscaped)
			{
			this.hasEscaped = true;
			this.gameStopped = true;
			
			for (Monster m : this.level.getMonsters())
				{
				m.stopWalk();
				}
			
			try 
				{
				Thread.sleep(3000);
				}
			catch (InterruptedException e)
				{
				}
			
			this.hasEscaped = false;
			this.nextMapLoading = true;
			
			try 
				{
				Thread.sleep(1000);
				}
			catch (InterruptedException e)
				{
				}
			
			// Get next level.
			this.startNextLevel();
			
			this.nextMapLoading = false;
			this.gameStopped = false;
			}
		}
	
	/**
	 * Creates the next level based on the actual level.
	 */
	private void startNextLevel()
		{
		// TODO -> Make some more things while creating a new level.
		Level nextLevel = new Level(this.loader, this.level.getLevelStage() + 1, this.level.getLevelWidth() + 1, this.level.getLevelHeight() + 1);
		this.level = nextLevel;
		
		// Set the player to the level start location.
		this.player.setLevel(this.level);
		this.player.setLocation(this.level.getLevelStartLocation());
		this.view.forceLocationUpdate();
		this.view.forceTileMapUpdate();
		}
	
	/**
	 * Returns if the player has escaped.
	 * @return true if the player has escaped.
	 */
	public boolean getPlayerHasEscaped()
		{
		return this.hasEscaped;
		}
	
	/**
	 * Returns if the game has been stopped.
	 * @return true if the game has been stopped.
	 */	
	public boolean getGameHasStopped()
		{
		return this.gameStopped;
		}
	
	/**
	 * Returns if the next level is loading.
	 * @return true if the next level is loading.
	 */	
	public boolean getNextLevelIsLoading()
		{
		return this.nextMapLoading;
		}
	}